package com.jvxb.demo.config;

import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;

/**
 * 1）redis序列化配置，Object序列化为Json，比自带的jdk序列化方式，具有更快的速度
 * 这个主要是根据redis存储的数据类型需求决定，key一般都是String，但是value可能不一样，一般有两种，String和 Object；
 * 如果k-v都是String类型，我们可以直接用 StringRedisTemplate，这个是官方建议的，也是最方便的，直接导入即用，无需多余配置！
 * 如果k-v是Object类型，则需要自定义 RedisTemplate
 *
 * 2）配置cache
 *
 * @author jvxb
 * @since 202-12-26
 */
@Configuration
@EnableCaching
public class RedisConfig {

    /**
     * 使用方式：直接引入
     * @Autowired 
     * RedisTemplate<String, Object> redisTemplate
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        // key采用String的序列化方式
        redisTemplate.setKeySerializer(stringRedisSerializer);
        // value序列化方式采用jackson
        redisTemplate.setValueSerializer(genericJackson2JsonRedisSerializer);
        // hash的key也采用String的序列化方式
        redisTemplate.setHashKeySerializer(stringRedisSerializer);
        //hash的value序列化方式采用jackson
        redisTemplate.setHashValueSerializer(genericJackson2JsonRedisSerializer);
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        return redisTemplate;
    }

    /**
     * 缓存管理器
     * 相关注解：
     * @Cacheable：使用缓存。在方法执行前如果有数据，则直接返回缓存数据；没有则调用方法并将方法返回值放进缓存。
     * @CachePut：更新缓存，将方法的返回值放到缓存中
     * @CacheEvict：清空缓存
     * 相关配置：
     * key：缓存key，可以不指定，默认为方法参数
     * value/cacheNames: 缓存key的前缀，必须指定
     * condition：调用前判断，缓存的条件。（condition为false时不进行缓存，默认true）
     * unless：执行后判断，不缓存的条件。 （unless为true时不进行缓存，默认false）
     * 相应示例：
     * @Cacheable(value = "menuById")
     * @Cacheable(value = "menuById", key = "#id")
     * @Cacheable(value = "menuById", key = "'id-' + #id", condition = "#id != null", unless = "#result == null")
     * @CachePut(value = "newMenu", key = "#id")
     * @CacheEvict(value = "menuById", key = "#id")
     */
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
        RedisCacheConfiguration defaultCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofHours(1))
                .serializeKeysWith(
                        RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair
                        .fromSerializer(new GenericJackson2JsonRedisSerializer()))
                .computePrefixWith(name -> name + ":");
        RedisCacheManager.RedisCacheManagerBuilder builder = RedisCacheManager.RedisCacheManagerBuilder
                .fromConnectionFactory(redisConnectionFactory).cacheDefaults(defaultCacheConfiguration);
        return builder.build();
    }
}
